﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using GPainter.Data;
using GPainter.ElementViews;
using GPainter.Events;
using GPainter.Interfaces;
using Point = System.Windows.Point;

namespace GPainter.ModelViewPresenter
{
    public enum Operations
    {
        Move,
        AddEdge
    }


    public class View
    {
        private readonly Canvas backGround; //поверхность рисования
        private readonly List<EdgeView> lstEdges = new List<EdgeView>(); //коллекция дуг, помещенных на канвас
        //три вида меню в зависимости от контекста вызова
        private readonly ContextMenu mStandart;
        private readonly ContextMenu menuEdge;
        private readonly ContextMenu menuNode;
        private readonly MenuItem mnAddEdge;
        private int NewEdgeCount; //количество добавленных дуг
        private int NewNodeCount; //количество добавленных вершин
        private UIElement _nodeClicked; //вершина на которой производится клик
        private bool drag;
        private bool drawEdge; //рисование дуги
        private MenuItem mnAddNode;

        public View(Canvas cnvs)
        {
            backGround = cnvs;
            //добавляем обработчики событий
            backGround.MouseLeftButtonDown += drSurface_MouseDown;
            backGround.MouseLeftButtonUp += drSurface_MouseUp;
            backGround.MouseMove += drSurface_MouseMove;
            backGround.MouseRightButtonDown += backGround_MouseRightButtonDown;
            backGround.KeyDown += backGround_KeyDown;
            //стандартное меню
            mStandart = new ContextMenu();
            backGround.ContextMenu = mStandart;
            //добавляем элементы контекстного меню
            //MenuItem mnAddNode = new MenuItem();
            //mnAddNode.Header = "Добавить сущность...";
            //mnAddNode.Click += new RoutedEventHandler(mnAddNode_Click);
            //backGround.ContextMenu.Items.Add(mnAddNode);
            mnAddEdge = new MenuItem {Header = "Добавить связь...", IsChecked = false};
            mnAddEdge.Click += mnAddEdge_Click;
            backGround.ContextMenu.Items.Add(mnAddEdge);
            //MenuItem mnPlanar = new MenuItem();
            //mnPlanar.Header = "Автоматическая укладка";
            //mnPlanar.Click += new RoutedEventHandler(mnPlanar_Click);
            //mnPlanar.IsEnabled = false;
            //backGround.ContextMenu.Items.Add(mnPlanar);
            //теперь формируем меню для дуг
            menuEdge = new ContextMenu();
            var mi = new MenuItem {Header = "Удалить связь"};
            mi.Click += mi_Click;
            menuEdge.Items.Add(mi);
            var miEdgeProp = new MenuItem {Header = "Свойства связи..."};
            miEdgeProp.Click += miEdgeProp_Click;
            menuEdge.Items.Add(miEdgeProp);
            //меню для для вершин
            menuNode = new ContextMenu();
            var menuItemDeleteNode = new MenuItem {Header = "Удалить сущность"};
            menuItemDeleteNode.Click += menuItemDeleteNode_Click;
            menuNode.Items.Add(menuItemDeleteNode);
            var menuItemNodeProp = new MenuItem {Header = "Свойства сущности..."};
            menuItemNodeProp.Click += menuItemNodeProp_Click;
            menuNode.Items.Add(menuItemNodeProp);
            //нет мультиселекта
            isMultySelect = false;
            currentOperation = Operations.Move;
        }

        private Operations currentOperation { get; set; }
        private bool isMultySelect { get; set; }

        public void InitializeView(int count) //процедура инициализации
        {
            NewNodeCount = count;
        }

        //заполнение тривьюшки у NodeControl
        //private void FillTreeView(TreeViewItem treeViewItem,List<MyTreeViewItem> Childs)
        //{
        //    if (Childs == null) return;
        //    foreach (MyTreeViewItem myTr in Childs)
        //    {
        //        TreeViewItem trItem = new TreeViewItem();
        //        trItem.Header = myTr.Header;
        //        treeViewItem.Items.Add(trItem);
        //        FillTreeView(trItem, myTr.Childs);
        //    }
        //}

        //рисование вершины
        public void DrawNode(INodeData node)
        {
            //double charLen = 12;
            //double correctLen = 10;
            //NodeView nd = new NodeView();
            //nd.OnRestoreNodePosition += CorrectCoords;
            //nd.Center = new Point();
            //nd.Center.X = node.Pos.X; //+ node.boundary.w / 2;
            //nd.Center.Y = node.Pos.Y;// + node.boundary.h / 2;
            //nd.textNode.Text = node.NodeText;
            ////nd.Width = charLen * node.nodeText.Length + correctLen;
            ////игнорируем ширину
            ////nd.w = node.boundary.w;
            //nd.Id = node.Id;
            ////игнорируем высоту
            ////добавляем дерево свойств
            //object  treeViewItems = node.GetType().GetProperty("visualTree").GetValue(node, null);
            //MyTreeViewItem PropertyList  = treeViewItems as MyTreeViewItem;
            //List<MyTreeViewItem> helpList = PropertyList.Childs;
            //foreach (MyTreeViewItem Mytr in helpList)
            //{
            //    TreeViewItem tr = new TreeViewItem();
            //    tr.Header = Mytr.Header;
            //    nd.PART_TrHierarhy.Items.Add(tr);//добавляем в тривью
            //    FillTreeView(tr,Mytr.Childs);  
            //}

            var viewNode = node.NodeView as UIElement;
            backGround.Children.Add(viewNode);
            Canvas.SetLeft(viewNode, node.Pos.X - node.Boundary.W/2);
            Canvas.SetTop(viewNode, node.Pos.Y - node.Boundary.H/2);

            Panel.SetZIndex(viewNode, 2);
        }

        //поиск нужного INodeView
        private INodeView FindNode(string id)
        {
            return backGround.Children.OfType<INodeView>().FirstOrDefault(viewNode => viewNode.DataNode.Id == id);
        }

        //вычисления для стрелочек
        //рисуем стрелочки
        private void DrawArrow(EdgeView edc, Point start, Point end)
        {
            //угол стрелочки
            double arrowAngle = 45;
            //считаем координаты вектора
            var dir = new Point();
            dir.X = end.X - start.X;
            dir.Y = end.Y - start.Y;
            //нормируем вектор
            double len = Math.Sqrt(Math.Pow(dir.X, 2) + Math.Pow(dir.Y, 2));
            dir.X /= len;
            dir.Y /= len;
            //считаем перпендикулярный
            var s = new Point(-dir.Y, dir.X);
            s.X *= len*(Math.Tan(arrowAngle/2*(Math.PI/180)));
            s.Y *= len*(Math.Tan(arrowAngle/2*(Math.PI/180)));
            var P0 = new Point(start.X - s.X, start.Y - s.Y);
            Point P1 = end;
            var P2 = new Point(start.X + s.X, start.Y + s.Y);
            edc.Arrow.Points.Clear();
            edc.Arrow.Points.Add(P0);
            edc.Arrow.Points.Add(P1);
            edc.Arrow.Points.Add(P2);
        }

        //подготовительные действия перед рисованием стрелочки
        public void PreDrawArrow(IEdgeData edge, Point pSource, Point pTarget)
        {
            double w = Math.Abs(pSource.X - pTarget.X),
                   h = Math.Abs(pSource.Y - pTarget.Y);
            double arrowLen = 10,
                   //длина стрелочки
                   angle = 0;
            double x = 0, y = 0;
            if (pSource.X < pTarget.X)
            {
                angle = Math.Atan(h/w);
                x = pTarget.X - arrowLen*Math.Cos(angle);
                y = YlineEq(pSource.X, pSource.Y, pTarget.X, pTarget.Y, x);
            }
            else if (pSource.X > pTarget.X)
            {
                angle = Math.Atan(h/w);
                x = pTarget.X + arrowLen*Math.Cos(angle);
                y = YlineEq(pSource.X, pSource.Y, pTarget.X, pTarget.Y, x);
            }
            else
            {
                x = pTarget.X;
                if (pSource.Y > pTarget.Y)
                {
                    y = pTarget.Y + arrowLen;
                }
                else
                {
                    y = pTarget.Y - arrowLen;
                }
            }
            EdgeView edc = FindEdge(edge.Id);
            DrawArrow(edc, new Point(x, y), pTarget);
        }


        public void DrawEdge(IEdgeData edge, INodeData sNode, INodeData tNode) //рисование дуги
        {
            var edc = new EdgeView(edge.Id);
            lstEdges.Add(edc);
            //добавляем текстблок
            edc.Txt.Text = edge.EdgeText;
            backGround.Children.Add(edc.Txt);
            Panel.SetZIndex(edc.Txt, 1);
            //добавляем текстблок для текста около начальной и конечной вершины
            edc.TxtStart.Text = edge.StartEdgeText;
            edc.TxtEnd.Text = edge.EndEdgeText;
            backGround.Children.Add(edc.TxtStart);
            backGround.Children.Add(edc.TxtEnd);
            Panel.SetZIndex(edc.TxtStart, 1);
            Panel.SetZIndex(edc.TxtEnd, 1);
            //добавляем стрелочки
            if (edge.ArrowHeadAtTarget)
            {
                backGround.Children.Add(edc.Arrow);
                Panel.SetZIndex(edc.Arrow, 0);
            }
            if (edge.ArrowHeadAtSource)
            {
            }
            //выбираем тип дуги
            if (edge.Curve == EdgeType.Linear)
            {
                backGround.Children.Add(edc.Line);
                Panel.SetZIndex(edc.Line, 0);
            }
            if (edge.Curve == EdgeType.Round)
            {
                backGround.Children.Add(edc.RoundedEdge);
            }
            DrawEdgeShape(edge, sNode, tNode, edc);
        }

        public void RefreshNode(INodeData node) //обновление вершины
        {
            if (node == null) return;
            //_nodeClicked = FindNode(node.Id);
            //if (_nodeClicked == null) return;
            //nodeClicked.Center = new Point();
            //nodeClicked.Center.X = node.Pos.X;
            //nodeClicked.Center.Y = node.Pos.Y;
            _nodeClicked = node.NodeView as UIElement;
            Canvas.SetLeft(_nodeClicked, node.Pos.X - node.Boundary.W/2); //- nodeClicked.ActualWidth / 2);
            Canvas.SetTop(_nodeClicked, node.Pos.Y - node.Boundary.H/2); //- nodeClicked.ActualHeight / 2);
        }

        //удаление вершины
        public void DeleteNode(INodeData node)
        {
            //  NodeView nc = FindNode(node.Id);
            backGround.Children.Remove(node.NodeView as UIElement);
            _selectedNodeId = "";
        }

        //поиск EdgeView

        private EdgeView FindEdge(string id)
        {
            return lstEdges.FirstOrDefault(eC => eC.Id == id);
        }

        //уравнение прямой по двум точкам относительно Y
        private double YlineEq(double x1, double y1, double x2, double y2, double x)
        {
            double y = y2 + (y1 - y2)*((x - x2)/(x1 - x2));
            return y;
        }

        //уравнение прямой по двум точкам относительно X
        private double XlineEq(double x1, double y1, double x2, double y2, double y)
        {
            double x = -1;
            if (y1 != y2)
            {
                x = x2 + (x1 - x2)*((y - y2)/(y1 - y2));
            }
            return x;
        }


        //получить центр отрезка
        private Point GetCenter(Point sourceP, Point targetP)
        {
            var p = new Point {X = (sourceP.X + targetP.X)/2, Y = (sourceP.Y + targetP.Y)/2};
            return p;
        }

        //вычисление точки пересечения
        private void SolveTPer(Point sourceP, Point targetP,
                               INodeData tNode, out Point result)
        {
            double h = Math.Abs(sourceP.Y - targetP.Y);
            double w = Math.Abs(sourceP.X - targetP.X);
            double x = 0;
            double actualHeight = FindNode(tNode.Id).DataNode.Boundary.H;
            result = new Point();
            if (sourceP.X < targetP.X)
            {
                if (sourceP.Y > targetP.Y)
                {
                    x = XlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, targetP.Y + actualHeight/2);
                    if (x >= targetP.X - tNode.Boundary.W/2)
                    {
                        result.X = x;
                        result.Y = targetP.Y + actualHeight/2;
                    }
                    else
                    {
                        result.X = targetP.X - tNode.Boundary.W/2;
                        result.Y = YlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, result.X);
                    }
                }
                else if (sourceP.Y <= targetP.Y)
                {
                    x = XlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, targetP.Y - actualHeight/2);
                    if (x == -1)
                    {
                        result.X = targetP.X - tNode.Boundary.W/2;
                        result.Y = targetP.Y;
                    }
                    else if (x >= targetP.X - tNode.Boundary.W/2)
                    {
                        result.X = x;
                        result.Y = targetP.Y - actualHeight/2;
                    }
                    else
                    {
                        result.X = targetP.X - tNode.Boundary.W/2;
                        result.Y = YlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, result.X);
                    }
                }
            }
            else //if (sourceP.X > targetP.X)
            {
                if (sourceP.Y > targetP.Y)
                {
                    x = XlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, targetP.Y + actualHeight/2);
                    if (x <= targetP.X + tNode.Boundary.W/2)
                    {
                        result.X = x;
                        result.Y = targetP.Y + actualHeight/2;
                    }
                    else
                    {
                        result.X = targetP.X + tNode.Boundary.W/2;
                        result.Y = YlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, result.X);
                    }
                }
                else if (sourceP.Y <= targetP.Y)
                {
                    x = XlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, targetP.Y - actualHeight/2);
                    if (x == -1)
                    {
                        result.X = targetP.X + tNode.Boundary.W/2;
                        result.Y = targetP.Y;
                    }
                    else if (x <= targetP.X + tNode.Boundary.W/2)
                    {
                        result.X = x;
                        result.Y = targetP.Y - actualHeight/2;
                    }
                    else
                    {
                        result.X = targetP.X + tNode.Boundary.W/2;
                        result.Y = YlineEq(sourceP.X, sourceP.Y, targetP.X, targetP.Y, result.X);
                    }
                }
            }
        }

        //общий метод добавления и обновления дуги
        public void DrawEdgeShape(IEdgeData edge, INodeData sNode, INodeData tNode, EdgeView edc)
        {
            if (sNode == null || tNode == null) return;
            if (FindNode(sNode.Id) == null || FindNode(tNode.Id) == null) return;
            if (edge.Curve == EdgeType.Linear)
            {
                edc.Line.X1 = sNode.Pos.X;
                edc.Line.Y1 = sNode.Pos.Y;
                edc.Line.X2 = tNode.Pos.X;
                edc.Line.Y2 = tNode.Pos.Y;
                //обновляем координаты текстблока 
                double x1 = Math.Min(edc.Line.X1, edc.Line.X2);
                double w = Math.Abs(edc.Line.X1 - edc.Line.X2);
                double y1 = Math.Min(edc.Line.Y1, edc.Line.Y2);
                double h = Math.Abs(edc.Line.Y1 - edc.Line.Y2);
                double txtCenterx = x1 + w/2;
                double txtCentery = y1 + h/2;
                Canvas.SetLeft(edc.Txt, txtCenterx - edc.Txt.ActualWidth/2);
                Canvas.SetTop(edc.Txt, txtCentery - edc.Txt.ActualHeight/2);
                //обновляем координаты других текстблоков - одна четвертая часть отрезка между серединой отрезка и вершиной в сторону вершины
                Canvas.SetLeft(edc.TxtStart, sNode.Pos.X + (txtCenterx - sNode.Pos.X)*0.5 - edc.TxtStart.ActualWidth/2);
                Canvas.SetTop(edc.TxtStart, sNode.Pos.Y + (txtCentery - sNode.Pos.Y)*0.5 - edc.TxtStart.ActualHeight/2);
                Canvas.SetLeft(edc.TxtEnd, tNode.Pos.X + (txtCenterx - tNode.Pos.X)*0.5 - edc.TxtEnd.ActualWidth/2);
                Canvas.SetTop(edc.TxtEnd, tNode.Pos.Y + (txtCentery - tNode.Pos.Y)*0.5 - edc.TxtEnd.ActualHeight/2);
                if (edge.ArrowHeadAtTarget)
                {
                    var p = new Point(0, 0);
                    SolveTPer(new Point(edc.Line.X1, edc.Line.Y1),
                              new Point(edc.Line.X2, edc.Line.Y2), tNode, out p);
                    PreDrawArrow(edge, new Point(edc.Line.X1, edc.Line.Y1), p);
                }
                if (edge.ArrowHeadAtSource)
                {
                }
            }
            //если дуга скругленная 
            if (edge.Curve == EdgeType.Round)
            {
                //центры вершин
                Data.Point startP = sNode.Pos;
                Data.Point targetP = tNode.Pos;
                var pTarget = new Point(targetP.X, targetP.Y);
                var pSource = new Point(startP.X, startP.Y);
                //вычисляем точки пересечения
                var pT = new Point(targetP.X, targetP.Y); //точка пересечения дуги с вершиной-целью
                var pS = new Point(startP.X, startP.Y); //точка пересечения дуги с вершиной-источником
                SolveTPer(pSource, pTarget, tNode, out pT);
                SolveTPer(pTarget, pSource, sNode, out pS);
                double h = Math.Abs(pT.Y - pS.Y);
                double w = Math.Abs(pT.X - pS.X);
                double diaX = Math.Sqrt(Math.Pow(h, 2) + Math.Pow(w, 2));
                edc.Pf.StartPoint = pS;
                edc.Arc.Point = pT;
                double Yrad = edge.RadY*30;
                edc.Arc.Size = new Size(diaX/2, Yrad);
                double angle = 0; //угол поворота эллипса
                if (w == 0)
                {
                    angle = 90;
                }
                else
                {
                    angle = Math.Atan(h/w)*(180/Math.PI);
                }
                if (pTarget.Y < pSource.Y)
                {
                    if (pTarget.X < pSource.X)
                    {
                        edc.Arc.RotationAngle = angle;
                    }
                    else
                    {
                        edc.Arc.RotationAngle = 180 - angle;
                    }
                }
                else
                {
                    if (pTarget.X > pSource.X)
                    {
                        edc.Arc.RotationAngle = angle;
                    }
                    else
                    {
                        edc.Arc.RotationAngle = 180 - angle;
                    }
                }
                edc.Arc.IsLargeArc = true;
                //посчитаем координаты текстблока
                var dir = new Point();
                dir.X = pTarget.X - pSource.X;
                dir.Y = pTarget.Y - pSource.Y;
                //нормируем вектор
                double len = Math.Sqrt(Math.Pow(dir.X, 2) + Math.Pow(dir.Y, 2));
                dir.X /= len;
                dir.Y /= len;
                //считаем перпендикулярный
                var s = new Point(-dir.Y, dir.X);
                s.X *= Yrad;
                s.Y *= Yrad;
                Point center = GetCenter(pSource, pTarget); //центр дуги
                //если проходит по верху
                if (edge.IsTop)
                {
                    edc.Arc.SweepDirection = SweepDirection.Clockwise;
                    Canvas.SetLeft(edc.Txt, center.X - s.X - edc.Txt.ActualWidth/2);
                    Canvas.SetTop(edc.Txt, center.Y - s.Y - edc.Txt.ActualHeight/2);
                    if (edge.ArrowHeadAtTarget)
                    {
                        s.X *= 5;
                        s.Y *= 5;
                        PreDrawArrow(edge, new Point(center.X - s.X, center.Y - s.Y), pT);
                    }
                    if (edge.ArrowHeadAtSource)
                    {
                    }
                }
                else
                {
                    edc.Arc.SweepDirection = SweepDirection.Counterclockwise;
                    Canvas.SetLeft(edc.Txt, center.X + s.X - edc.Txt.ActualWidth/2);
                    Canvas.SetTop(edc.Txt, center.Y + s.Y - edc.Txt.ActualHeight/2);
                    if (edge.ArrowHeadAtTarget)
                    {
                        s.X *= 5;
                        s.Y *= 5;
                        PreDrawArrow(edge, new Point(center.X + s.X, center.Y + s.Y), pT);
                    }
                    if (edge.ArrowHeadAtSource)
                    {
                    }
                }
            }
        }

        public void RefreshEdge(IEdgeData edge, INodeData sNode, INodeData tNode) //обновление дуги
        {
            EdgeView edc = FindEdge(edge.Id);
            if (edc == null) return;
            DrawEdgeShape(edge, sNode, tNode, edc);
        }

        public void DeleteEdge(IEdgeData e)
        {
            EdgeView eC = FindEdge(e.Id);
            backGround.Children.Remove(eC.Txt);
            backGround.Children.Remove(eC.TxtStart);
            backGround.Children.Remove(eC.TxtEnd);
            backGround.Children.Remove(eC.Arrow);
            if (e.Curve == EdgeType.Round)
            {
                backGround.Children.Remove(eC.RoundedEdge);
            }
            else
            {
                backGround.Children.Remove(eC.Line);
            }
            lstEdges.Remove(eC);
            _selectedEdgeId = "";
        }

        //коррекция координат
        public void CorrectCoords(object sender, RoutedEventArgs e)
        {
            var nc = sender as INodeView;
            if (nc != null)
            {
                //обновляем координаты и дуги
                Canvas.SetLeft(nc as UIElement, nc.DataNode.Pos.X - nc.DataNode.Boundary.W/2);
                Canvas.SetTop(nc as UIElement, nc.DataNode.Pos.Y - nc.DataNode.Boundary.H/2);
                UpdateAllNodeEdges(this, new NodeAddEventArgs(nc.DataNode.Id, "Node"));
                //обновляем выделенные вершинную вершину
                if (!nc.IsSelected)
                {
                    _selectedNodeCount = 0;
                    DeSelectAll();
                    _selectedNodeId = nc.DataNode.Id;
                    nc.IsSelected = true;
                    backGround.ContextMenu = menuNode;
                    OnSelectNode(this, new NodeAddEventArgs(_selectedNodeId, "Node"));
                }
            }
        }

        public void SetMultySelect(bool multySelect) //режим мультиселект?
        {
            isMultySelect = multySelect;
            if (isMultySelect)
            {
                (menuEdge.Items[0] as MenuItem).IsEnabled = false;
                (menuNode.Items[0] as MenuItem).IsEnabled = false;
                (menuNode.Items[1] as MenuItem).IsEnabled = false;
                //(menuNode.Items[2] as MenuItem).IsEnabled = false;
            }
            else
            {
                (menuEdge.Items[0] as MenuItem).IsEnabled = true;
                (menuNode.Items[0] as MenuItem).IsEnabled = true;
                (menuNode.Items[1] as MenuItem).IsEnabled = true;
                //(menuNode.Items[2] as MenuItem).IsEnabled = true;
            }
        }

        private void backGround_KeyDown(object sender, KeyEventArgs e)
        {
            if (!isMultySelect)
            {
                if (e.Key == Key.Delete)
                {
                    if (_selectedNodeId != "")
                    {
                        if (MessageBox.Show("Удалить выбранную вершину?", "Удаление вершины", MessageBoxButton.YesNo,
                                            MessageBoxImage.Question) == MessageBoxResult.Yes)
                        {
                            OnDeleteNode(this, new NodeDeleteEventArgs(_selectedNodeId));
                        }
                    }
                    if (_selectedEdgeId == "")
                    {
                        if (MessageBox.Show("Удалить выбранную дугу?", "Удаление дуги", MessageBoxButton.YesNo,
                                            MessageBoxImage.Question) == MessageBoxResult.Yes)
                        {
                            OnDeleteEdge(this, new EdgeDeleteEventArgs(_selectedEdgeId, "", "",
                                                                       FindEdge(_selectedEdgeId).Txt.Text));
                        }
                    }
                }
            }
        }

        #region Implements ITool

        private void mnAddEdge_Click(object sender, RoutedEventArgs e)
        {
            if (!mnAddEdge.IsChecked)
            {
                NewEdgeCount++;
                //mnAddEdgeSub.IsChecked = false;
                currentOperation = Operations.AddEdge;
            }
            else
            {
                currentOperation = Operations.Move;
            }
            //  OnChangeOperation(this, new OperationEventArgs(currentOperation));
            mnAddEdge.IsChecked = !mnAddEdge.IsChecked;
        }

        //добавление вершины
        public void mnAddNode_Click(object sender, RoutedEventArgs e)
        {
            NewNodeCount++;
            AddNewNode(sender, new NodeAddEventArgs("NewNode" +
                                                    NewNodeCount, "Node"));
        }

        private void backGround_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            drSurface_MouseDown(this, e); //выделение вершины но без drag&drop
        }

        private void mi_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedEdgeId == "") return;
            if (MessageBox.Show("Удалить выбранную связь?", "Удаление связи", MessageBoxButton.YesNo,
                                MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                OnDeleteEdge(this, new EdgeDeleteEventArgs(_selectedEdgeId, "", "",
                                                           FindEdge(_selectedEdgeId).Txt.Text));
            }
        }

        private void menuItemDeleteNode_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedNodeId == "") return;
            //if (MessageBox.Show("Удалить выбранную сущность?", "Удаление сущности", MessageBoxButton.YesNo,
            //    MessageBoxImage.Question) == MessageBoxResult.Yes)
            //{
            OnDeleteNode(this, new NodeDeleteEventArgs(_selectedNodeId));
            //}
        }

        private void mnPlanar_Click(object sender, RoutedEventArgs e)
        {
            OnAutomaticLayout(this, new EventArgs());
        }

        //свойства вершин
        private void menuItemNodeProp_Click(object sender, RoutedEventArgs e)
        {
        }

        //свойства дуг
        private void miEdgeProp_Click(object sender, RoutedEventArgs e)
        {
        }

        #endregion

        #region события, оповещающие презентор о внешних изменениях

        public event EventHandler<NodeMoveEventArgs> ChangeNodePosition;
        public event EventHandler<NodeAddEventArgs> AddNewNode;
        public event EventHandler<EdgeAddEventArgs> AddNewEdge;
        public event EventHandler<EdgeDeleteEventArgs> OnDeleteEdge; //удаление дуги
        public event EventHandler<NodeDeleteEventArgs> OnDeleteNode; //удаление вершины
        public event EventHandler<NodeAddEventArgs> OnSelectNode; //выделение вершины
        public event EventHandler<OperationEventArgs> OnChangeOperation; //изменение текущей операции
        public event EventHandler<NodeAddEventArgs> UpdateAllNodeEdges; //обновить все дуги вершины
        public event EventHandler<EventArgs> OnDeselectAll; //деселект всех вершин
        public event EventHandler<NodeScrollEventArgs> OnScroll; //
        public event EventHandler<EventArgs> OnAutomaticLayout; //автоматическая укладка

        #endregion

        #region смаршрутизированные события на канвас

        private Line _edgeClicked; //ребро, на которое кликнулись
        private bool _multipleDrag; //режим мультиперетаскивания
        private bool _multipleMove; //режим рисования прямоугольника выделения
        private Point _pointClicked;
        private Rectangle _rect; //прямоугольник для выделения
        private string _selectedEdgeId = ""; //ID выделенной дуги
        private int _selectedNodeCount; //количество выделенных вершин
        private string _selectedNodeId = ""; //ID выделенной вершины

        //принадлежит ли вершина выделению
        private double l, t; //параметры прямоугольника

        private void DeSelectAll()
        {
            if (_selectedEdgeId != "")
            {
                FindEdge(_selectedEdgeId).IsSelected = false;
            }
            if (_selectedNodeId != "")
            {
                FindNode(_selectedNodeId).IsSelected = false;
            }
            foreach (UIElement ui in backGround.Children)
            {
                var n = ui as NodeView;
                if (n != null)
                {
                    n.IsSelected = false;
                }
            }
        }

        public void drSurface_MouseDown(Object Sender, MouseButtonEventArgs e)
        {
            drag = false;
            drawEdge = false;
            _multipleMove = false;
            _pointClicked = e.GetPosition(backGround);
            var viewNode = (e.Source as INodeView);
            _nodeClicked = (viewNode != null) ? viewNode as UIElement : null;
            var txtEdge = e.Source as TextBlock; //кликнулись ли мы на дугу
            //если режим мультиселекта
            if (_nodeClicked == null)
            {
                _selectedNodeCount = 0;
                DeSelectAll();
            }
            //рисование прямоугольника
            if (isMultySelect && _nodeClicked == null && e.RightButton != MouseButtonState.Pressed &&
                txtEdge == null)
            {
                _selectedNodeCount = 0;
                _multipleMove = true;
                //рисуем прямоугольник покрывающий вершинки
                _rect = new Rectangle();
                var dCol = new DoubleCollection(2) {1, 2};
                _rect.StrokeDashArray = dCol;
                _rect.Fill = Brushes.Transparent;
                _rect.StrokeThickness = 2;
                _rect.Stroke = Brushes.Black;
                backGround.Children.Add(_rect);
                _rect.Width = 1;
                _rect.Height = 1;
                Canvas.SetTop(_rect, _pointClicked.Y);
                Canvas.SetLeft(_rect, _pointClicked.X);
                backGround.ContextMenu = mStandart;
                return;
            }
            //режим мультиперемещения
            if (_selectedNodeCount > 1 && isMultySelect && _nodeClicked != null
                && e.RightButton != MouseButtonState.Pressed)
            {
                if ((_nodeClicked as INodeView).IsSelected)
                {
                    _multipleDrag = true;
                    //переделать в связи с новым механизмом
                    //foreach (UIElement ui in backGround.Children)
                    //{
                    //    var n = ui as INodeView;
                    //    if (n != null)
                    //    {
                    //        if (n.IsSelected)
                    //        {
                    //            n.X = n.Center.X;
                    //            n.Y = n.Center.Y;
                    //        }
                    //    }
                    //}
                    backGround.ContextMenu = menuNode;
                    return;
                }
            }
            //конец режима мультиселекта
            //убираем выделенные элементы графа
            DeSelectAll();
            OnDeselectAll(this, new EventArgs());
            //режим перемещения вершин

            if (_nodeClicked != null)
            {
                _selectedNodeId = (_nodeClicked as INodeView).DataNode.Id;
                (_nodeClicked as INodeView).IsSelected = true;
                backGround.ContextMenu = menuNode;
                OnSelectNode(this, new NodeAddEventArgs(_selectedNodeId, "Node"));
                if (e.RightButton == MouseButtonState.Pressed) return;
            }
            if (_nodeClicked != null && currentOperation == Operations.Move)
            {
                drag = true;
                drawEdge = false;
                return;
            }


            //режим добавления дуги
            //if (mnAddEdge.IsChecked || mnAddEdgeSub.IsChecked)
            if (currentOperation == Operations.AddEdge && e.RightButton != MouseButtonState.Pressed)
            {
                NewEdgeCount++;
                backGround.ContextMenu = mStandart;
                drag = false;
                drawEdge = true;
                if (_nodeClicked == null) return;
                //nodeClicked.PART_Brd.Cursor = Cursors.Hand;
                var edc = new EdgeView("NewEdge" + NewEdgeCount.ToString());
                lstEdges.Add(edc);
                edc.Line.X1 = _pointClicked.X;
                edc.Line.Y1 = _pointClicked.Y;
                edc.Line.X2 = _pointClicked.X;
                edc.Line.Y2 = _pointClicked.Y;
                edc.Line.Stroke = Brushes.Black;
                var dCol = new DoubleCollection(2) {1, 2};
                edc.Line.StrokeDashArray = dCol;
                edc.Line.StrokeThickness = 2;
                backGround.Children.Add(edc.Line);
                Panel.SetZIndex(edc.Line, 0);
                return;
            }
            //в случае когда кликаемся на дугу

            _edgeClicked = e.Source as Line;

            if (_edgeClicked == null && txtEdge == null)
            {
                _selectedEdgeId = "";
                _selectedNodeId = "";
                backGround.ContextMenu = mStandart;
                return;
            }

            //находим по линии
            if (_edgeClicked != null)
            {
                EdgeView ec = lstEdges.Find(EdC => (EdC.Line == _edgeClicked));
                ec.IsSelected = true;
                _selectedEdgeId = ec.Id;
            }
            //находим по текстбоксу
            if (txtEdge != null)
            {
                EdgeView ec = lstEdges.Find(EdC => (EdC.Txt == txtEdge));
                if (ec != null)
                {
                    ec.IsSelected = true;
                    _selectedEdgeId = ec.Id;
                }
            }
            backGround.ContextMenu = menuEdge;
        }


        public void SelectNode(string nodeID)
        {
            DeSelectAll();
            INodeView selectedNode = FindNode(nodeID);
            if (selectedNode == null) return;
            _selectedNodeId = selectedNode.DataNode.Id;
            selectedNode.IsSelected = true;
            backGround.ContextMenu = menuNode;
        }

        private bool IsNodeBelong(INodeView nc)
        {
            double left = nc.DataNode.Pos.X - nc.DataNode.Boundary.W/2;
            double rigth = left + nc.DataNode.Boundary.W;
            double top = nc.DataNode.Pos.Y - nc.DataNode.Boundary.H/2;
            double bottom = top + nc.DataNode.Boundary.H;
            return ((left >= l) && (rigth <= l + _rect.Width) && (top >= t) && (bottom <= t + _rect.Height));
        }

        public void drSurface_MouseUp(Object Sender, MouseButtonEventArgs e)
        {
            //drag = false;
            //if (drawEdge)
            //{
            //    drawEdge = false;
            //    var ncEnd = e.Source as INodeView;//вершина - конец драг анд дропа
            //    var NewId = "NewEdge" + NewEdgeCount;
            //    var edc = FindEdge(NewId);//текущая дуга
            //    if (ncEnd != null)
            //    {
            //        if (edc == null) return;
            //        edc.line.StrokeDashArray = null;
            //        //обновляем координаты текстблоков
            //        //if (currentOperation == "sub") edc.txt.Text = "sub";
            //        var x1 = Math.Min(edc.line.X1, edc.line.X2);
            //        var w = Math.Abs(edc.line.X1 - edc.line.X2);
            //        var y1 = Math.Min(edc.line.Y1, edc.line.Y2);
            //        var h = Math.Abs(edc.line.Y1 - edc.line.Y2);
            //        var txtCenterx = x1 + w / 2;
            //        var txtCentery = y1 + h / 2;
            //        Canvas.SetLeft(edc.txt, txtCenterx - edc.txt.ActualWidth / 2);
            //        Canvas.SetTop(edc.txt, txtCentery - edc.txt.ActualHeight / 2);
            //        Canvas.SetLeft(edc.txtStart, edc.line.X1 + (txtCenterx - edc.line.X1) * 0.5 - edc.txtStart.ActualWidth / 2);
            //        Canvas.SetTop(edc.txtStart, edc.line.Y1 + (txtCentery - edc.line.Y1) * 0.5 - edc.txtStart.ActualHeight / 2);
            //        Canvas.SetLeft(edc.txtEnd, edc.line.X2 + (txtCenterx - edc.line.X2) * 0.5 - edc.txtEnd.ActualWidth / 2);
            //        Canvas.SetTop(edc.txtEnd, edc.line.Y2 + (txtCentery - edc.line.Y2) * 0.5 - edc.txtEnd.ActualHeight / 2);
            //        //добавляем текстблоки
            //        edc.txtStart.Text = "1";
            //        edc.txtEnd.Text = "0..1";
            //        backGround.Children.Add(edc.txt);
            //        Canvas.SetZIndex(edc.txt, 1);
            //        backGround.Children.Add(edc.txtStart);
            //        Canvas.SetZIndex(edc.txtStart, 1);
            //        backGround.Children.Add(edc.txtEnd);
            //        Canvas.SetZIndex(edc.txtEnd, 1);
            //        edc.isSelected = true;
            //        _selectedEdgeId = NewId;
            //        //добавляем стрелочки по умолчанию
            //        //backGround.Children.Add(edc.tArrow);
            //        //Canvas.SetZIndex(edc.tArrow,0);
            //        //можем ли добавить дугу
            //        var edAddEvArgs = new EdgeAddEventArgs("NewEdge" + NewEdgeCount.ToString(),
            //            (_nodeClicked as INodeView).NodeData.Id, ncEnd.NodeData.Id, "Edge") { text = "" };
            //        //if (currentOperation == "sub") edAddEvArgs.text = "sub";
            //        //if (currentOperation == "is_a") edAddEvArgs.text = "is_a";
            //        //оповещаем презентор о добавлении новой дуги
            //        AddNewEdge(this, edAddEvArgs);
            //        mnAddEdge.IsChecked = !mnAddEdge.IsChecked;
            //        currentOperation = Operations.Move;
            //    }
            //    else
            //    {
            //        if (edc == null) return;
            //        lstEdges.Remove(edc);
            //        backGround.Children.Remove(edc.line);
            //        //mnAddEdge.IsChecked = false;
            //        //mnAddEdgeSub.IsChecked = false;
            //    }
            //}
            ////
            //if (_multipleMove)
            //{
            //    _selectedNodeCount = 0;
            //    OnDeselectAll(this, new EventArgs());
            //    foreach (UIElement ui in backGround.Children)
            //    {
            //        var n = ui as INodeView;
            //        if (n != null)
            //        {
            //            if (IsNodeBelong(n))
            //            {
            //                n.IsSelected = true;
            //                _selectedNodeCount++;
            //                OnSelectNode(this, new NodeAddEventArgs(n.NodeData.Id, "Node"));
            //            }
            //            else
            //            {
            //                n.IsSelected = false;
            //            }
            //        }
            //    }
            //    backGround.Children.Remove(_rect);
            //    _multipleMove = false;
            //}
            //_multipleDrag = false;
        }

        public void drSurface_MouseMove(Object Sender, MouseEventArgs e)
        {
            //System.Windows.Point p = e.GetPosition(backGround);
            ////если меняем положение вершины
            //if (drag && e.LeftButton == MouseButtonState.Pressed)
            //{
            //    if (_selectedNodeId != "")
            //    {
            //        var nn = FindNode(_selectedNodeId);
            //        //скроллинг
            //        if (p.X <= nn.NodeData.Boundary.w / 2 || p.Y <= nn.NodeData.Boundary.h / 2)
            //        {
            //            return;
            //        }
            //        ChangeNodePosition(this, new NodeMoveEventArgs(_selectedNodeId, p));
            //    }
            //}
            //////если рисуем новую дугу
            //EdgeView edc;
            //if (drawEdge && e.LeftButton == MouseButtonState.Pressed)
            //{
            //    edc = FindEdge("NewEdge" + NewEdgeCount);
            //    if (edc == null) return;
            //    edc.line.X2 = p.X;
            //    edc.line.Y2 = p.Y;
            //    DeSelectAll();
            //}
            //else if (drawEdge && e.LeftButton != MouseButtonState.Pressed)
            //{
            //    edc = FindEdge("NewEdge" + NewEdgeCount.ToString());
            //    if (edc == null) return;
            //    drawEdge = false;
            //    lstEdges.Remove(edc);
            //    backGround.Children.Remove(edc.line);
            //}

            ////рисование прямоугольника
            //if (_multipleMove && e.LeftButton == MouseButtonState.Pressed)
            //{
            //    var dx = p.X - _pointClicked.X;
            //    var dy = p.Y - _pointClicked.Y;
            //    _rect.Width = Math.Abs(dx);
            //    _rect.Height = Math.Abs(dy);
            //    if (dx > 0)
            //    {
            //        Canvas.SetLeft(_rect, _pointClicked.X);
            //        l = _pointClicked.X;
            //    }
            //    else
            //    {
            //        Canvas.SetLeft(_rect, p.X);
            //        l = p.X;
            //    }
            //    if (dy > 0)
            //    {
            //        Canvas.SetTop(_rect, _pointClicked.Y);
            //        t = _pointClicked.Y;

            //    }
            //    else
            //    {
            //        Canvas.SetTop(_rect, p.Y);
            //        t = p.Y;
            //    }
            //}
            //else if (_multipleMove && e.LeftButton != MouseButtonState.Pressed)
            //{
            //    _selectedNodeCount = 0;
            //    OnDeselectAll(this, new EventArgs());
            //    foreach (UIElement ui in backGround.Children)
            //    {
            //        var n = ui as INodeView;
            //        if (n != null)
            //        {
            //            if (IsNodeBelong(n))
            //            {
            //                n.IsSelected = true;
            //                _selectedNodeCount++;
            //                OnSelectNode(this, new NodeAddEventArgs(n.NodeData.Id, "Node"));
            //            }
            //            else
            //            {
            //                n.IsSelected = false;
            //            }
            //        }
            //    }
            //    backGround.Children.Remove(_rect);
            //    _multipleMove = false;
            //}
            ////перемещение выделенных вершин
            //if (_multipleDrag && e.LeftButton == MouseButtonState.Pressed)
            //{
            //    double dx = p.X - _pointClicked.X;
            //    double dy = p.Y - _pointClicked.Y;
            //    foreach (UIElement ui in backGround.Children)
            //    {
            //        var n = ui as INodeView;
            //        if (n != null)
            //        {
            //            if (n.IsSelected)
            //            {
            //                var newPos = new System.Windows.Point { X = n.NodeData.Pos.X + dx, Y = n.NodeData.Pos.Y + dy };
            //                if (newPos.X <= n.NodeData.Boundary.w / 2 || newPos.Y <= n.NodeData.Boundary.h / 2)
            //                {
            //                    return;
            //                }
            //                ChangeNodePosition(this, new NodeMoveEventArgs(n.NodeData.Id, newPos));
            //            }
            //        }
            //    }
            //}
        }

        #endregion
    }
}